﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using ThingyCloudWPF.Network.Messages;

namespace ThingyCloudWPF.Network
{
	public class MessageServer
	{
		/// <summary>
		/// The private static instance of this class, singleton usage
		/// </summary>
		private static MessageServer instance;

		/// <summary>
		/// Collection with all client sockets
		/// </summary>
        private List<Socket> sockets;

		/// <summary>
		/// Collection with all messages that are queued
		/// </summary>
        private List<Message> messages;

		/// <summary>
		/// Whether listening should be aborted
		/// </summary>
		private bool abort;

		/// <summary>
		/// The tcp listener
		/// </summary>
		private TcpListener listener;

		/// <summary>
		/// Listener thread, accepts incoming connections
		/// </summary>
		private Thread listenerThread;
		
		/// <summary>
		/// Sender thread, sends messages to all clients
		/// </summary>
		private Thread senderThread;

		/// <summary>
		/// An event to register to for when a new event was queued
		/// </summary>
		public event EventHandler<Events.MessageQueuedArgs> MessageQueued;

		/// <summary>
		/// Private constructor, we can only have one message server
		/// </summary>
		private MessageServer()
		{
			abort = false;

			// Initialize members
			sockets = new List<Socket>();
			messages = new List<Message>();
			listener = new TcpListener(new IPAddress(new byte[] { 0, 0, 0, 0 }), Globals.Messageport);

			// Prepare threads for listening and sending
			listenerThread = new Thread(new ThreadStart(Listen));
			senderThread = new Thread(new ThreadStart(Send));

			// Start listening!
			//listenerThread.Start();
			//senderThread.Start();

			Console.WriteLine("MessageServer: listening");
		}

		/// <summary>
		/// Singleton functionality, we can only have one active message server
		/// </summary>
		/// <returns></returns>
		public static MessageServer Instance
		{
			get
			{
				if (instance == null)
					instance = new MessageServer();

				return instance;
			}
		}

		/// <summary>
		/// Tell the listener and sender threads to stop
		/// </summary>
		public void Stop()
		{
			Debug.WriteLine("MessageServer: aborting operations");

			abort = true;

			// Stop the sending thread nicely
			senderThread.Join();

			// Kill listener thread
			listener.Stop();
			listenerThread.Join();
		}

		/// <summary>
		/// Queue a new message to be sent to all the sockets
		/// </summary>
		/// <param name="message"></param>
		public void Queue(Message message)
		{
			/*
			lock (messages)
			{
				messages.Add(message);
			}
			*/

			// Fire event that a new message was queued
			MessageQueued(this, new Events.MessageQueuedArgs(message));
		}

		/// <summary>
		/// Construct the message that we must send to each socket
		/// </summary>
		/// <returns></returns>
		/*
		private string GetMessage()
		{
			string messageString = "";

			// Lock on messages object
			lock (messages)
			{
				foreach (Message message in messages)
				{
					messageString += message.ToMessageString() + "|";
				}

				messages.Clear();
			}

			return messageString;
		}
		*/

		/// <summary>
		/// Thread-invoked method to start listening for incoming connections. Adds them to a collection of sockets
		/// that all are being informed with new messages
		/// </summary>
		private void Listen()
		{
			Debug.WriteLine(string.Format("MessageServer listening on port {0}", Globals.Messageport));
 
			// Start listening!
			listener.Start();

			while (!abort)
			{
				try
				{
					// Blocks but continues if there is a new socket
					Socket socket = listener.AcceptSocket();
					//lock (sockets)
					{
						Debug.WriteLine(string.Format("MessageServer: incoming connection from {0}", socket.RemoteEndPoint));
						socket.ReceiveTimeout = 1500;
						socket.SendTimeout = 1500;
						sockets.Add(socket);
					}
				}
				catch (Exception e)
				{
					Console.WriteLine(e);
				}
			}
		}

		/// <summary>
		/// Thread function that will periodically send all queued messages
		/// </summary>
		private void Send()
		{
			while (!abort)
			{
				if (messages.Count == 0)
				{
					Thread.Sleep(1000 / 30);
					continue;
				}

				// Copy sockets to a socket loop
				Socket[] socketLoop;
				//lock (sockets)
				{
					socketLoop = new Socket[sockets.Count];
					sockets.CopyTo(socketLoop);
				}

				// Copy all messages
				string msg = "";
				lock (messages)
				{
					// Concatenate the message string
					foreach (Message m in messages)
					{
						//if (!m.GetType().FullName.Contains("ActiveIdsMessage"))
							//continue;

						if (!m.GetType().FullName.Contains("ThingyMoveMessage"))
						{
							Debug.WriteLine(m.ToMessageString());
						}

						msg += m.ToMessageString() + "|";
						//Console.WriteLine(m.ToMessageString());
					}

					// Done with these. Clear 'em and continue!
					messages.Clear();
				}

				// Now we have to send this string to all sockets
				List<Socket> removeSockets = new List<Socket>();
				foreach (Socket socket in socketLoop)
				{
					// Remove if we can´t send
					if (!socket.Connected)
					{
						removeSockets.Add(socket);
						continue;
					}

					// Need a byte-array! Prepare and send.
					byte[] data = Encoding.ASCII.GetBytes(msg);
					try
					{
						//Debug.WriteLine(string.Format("Sending {0} bytes of data to {1}", data.Length, socket.RemoteEndPoint));
						int sent = socket.Send(data);
						if (sent != data.Length)
						{
							Debug.WriteLine("MessageServer: Amount of sent data was not equal to the length of the data!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111111111oneoneone");
						}
					}
					catch (SocketException se)
					{
						if (se.SocketErrorCode != SocketError.TimedOut)
							removeSockets.Add(socket);
					}
					catch (Exception e)
					{
						// This socket failed us! Bastard.. Remove it
						removeSockets.Add(socket);
					}
				}

				// Now remove failed sockets
				//lock (sockets)
				{
					foreach (Socket s in removeSockets)
					{
						Debug.WriteLine(string.Format("MessageServer: Socket no longer connected or working properly, removing {0}", s.RemoteEndPoint));
						sockets.Remove(s);
					}
				}

				//Console.WriteLine(msg);
				Thread.Sleep(1000 / 30);

                /*
				// Initialization
				int starttime = DateTime.Now.Millisecond;

				// Send messages 30 times per second
				int timediff = DateTime.Now.Millisecond - starttime;
				Thread.Sleep(Math.Max(0, (1000 / 30) - timediff));
                */
			}
		}
	}
}